--- id: TASK-004 title: Editor rebuilds full visual model twice per keystroke (O(document)) status: "\U0001F3C1 Done" assignee: [] created_date: '2026-06-28 18:38' updated_date: '2026-06-30 00:49' labels: - perf - release-1 dependencies: [] priority: low ordinal: 4000 --- ## Description Every InsertRune→followCursor→buildVisual rescans+rewraps all lines; MoveUp/MoveDown call buildVisual then followCursor calls it again (2 full scans/vertical keystroke). Fine for notes, costly for long docs in the writing canvas. Fix: thread one []vrow through move→followCursorWith(rows); longer term cache the visual model, invalidate on edit/resize. ## Acceptance Criteria - [x] #1 at most one buildVisual per keystroke ## Implementation Plan 1. RED: add buildCount instrumentation + tests asserting MoveUp/MoveDown/MoveToVisual each call buildVisual at most once per keystroke. 2. GREEN: extract followCursorWith(rows []vrow); have followCursor() = followCursorWith(buildVisual()). Thread the single rows slice from MoveUp/MoveDown/MoveToVisual into followCursorWith instead of re-scanning. 3. Verify suite + vet green; check AC #1. ## Implementation Notes Extracted followCursorWith(rows); MoveUp/MoveDown/MoveToVisual now scan once and reuse the rows for scroll-follow (was 2 scans/vertical keystroke). buildCount instrumentation + 3 perf tests assert <=1 buildVisual per move. Suite + vet green.